Skip to content

[3.0] Misc fixes - #9302

Merged
Sesquipedalian merged 23 commits into
SimpleMachines:release-3.0from
Sesquipedalian:3.0/upgrader_login
Sep 5, 2026
Merged

Sesquipedalian merged 23 commits into
SimpleMachines:release-3.0from
Sesquipedalian:3.0/upgrader_login

Conversation

@Sesquipedalian

@Sesquipedalian Sesquipedalian commented Jul 18, 2026 •

Copy link
Copy Markdown
Member
  1. Security fix: uses hash_equals() in SMF\Maintenance\Maintenance::loginAdmin() and SMF\Maintenance\Maintenance::loginWithDatabasePassword() methods.
  2. Logs correct info about who ran the upgrader. Previously, we were doing some silly things with loading and unloading User::$me. Not only was that unnecessary, and not only could it produce inaccurate info in the logs under certain circumstances, but it also further complicated the problems with getting the admin's login cookie right by the time the upgrade was complete.
  3. Moves SMF\Actions\Login2::validatePasswordFlood() to SMF\Security::validatePasswordFlood().
  4. Implements SMF\Security::checkPassword().
  5. Fixes bugs with handling login security tokens in the upgrader.
  6. Point 5 ultimately led to replacing Maintenance::$context with just plain old Utils::$context. Upon closer inspection, I realized that there was neither any need nor any benefit to having a separate version of $context; it just caused headaches when using other code, such as SecurityToken::create(), that is already designed to work with Utils::$context.

@Sesquipedalian
Sesquipedalian force-pushed the 3.0/upgrader_login branch 4 times, most recently from cbac018 to a73718e Compare July 18, 2026 21:42
@jdarwood007 jdarwood007 added this to the 3.0 Alpha 5 milestone Jul 18, 2026
@Sesquipedalian

Copy link
Copy Markdown
Member Author

Okay, I think I am done messing with this PR.

@sbulen, would you care to test whether it solves the problem of not being logged after the upgrader exits?

@Sesquipedalian
Sesquipedalian force-pushed the 3.0/upgrader_login branch 10 times, most recently from 8be3518 to 1f2c789 Compare July 19, 2026 19:10
@sbulen

sbulen commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

2.1 => 3.0

Fatal error: Uncaught Error: Cannot access uninitialized non-nullable property SMF\User::$username by reference in D:\wamp64\www\van2130\Sources\User.php:807 Stack trace: # 0 [internal function]: SMF\User->$member_name::get() # 1 D:\wamp64\www\van2130\Sources\Utils.php(1539): json_encode(Array, 0, 512) # 2 D:\wamp64\www\van2130\Sources\Services\ErrorHandlerService.php(286): SMF\Utils::jsonEncode(Array) # 3 D:\wamp64\www\van2130\Sources\Services\ErrorHandlerService.php(185): SMF\Services\ErrorHandlerService->log('Cannot access u...', 'general', 'D:/wamp64/www/v...', 807, Array) # 4 D:\wamp64\www\van2130\Sources\ErrorHandler.php(104): SMF\Services\ErrorHandlerService->catch(Object(Error)) # 5 [internal function]: SMF\ErrorHandler::catch(Object(Error)) # 6 {main} thrown in D:\wamp64\www\van2130\Sources\User.php on line 807

  • PHP, MySQL, Apache logs: nothing
  • Upgrader log: nothing

@Sesquipedalian

Sesquipedalian commented Jul 24, 2026 •

Copy link
Copy Markdown
Member Author

Well, phooey. I must have broken something in my tinkering after I first pushed. I suppose that's what I get for trying to code while sleepy.

@Sesquipedalian

Sesquipedalian commented Aug 5, 2026 •

Copy link
Copy Markdown
Member Author

@sbulen:

  1. I've made changes to this PR. Have they fixed the error about uninitialized properties?
  2. If so, does this PR solve the problem of not being logged after the upgrader exits?

@sbulen

sbulen commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

2.1 => 3.0

Getting a different WSOD:

Fatal error: Uncaught Error: Call to a member function allowedTo() on array in D:\wamp64\www\van2130\Sources\Maintenance\Maintenance.php:661 Stack trace: 0 D:\wamp64\www\van2130\Sources\Maintenance\Tools\Upgrade.php(819): SMF\Maintenance\Maintenance::loginAdmin('admin_shawnb', Object(SensitiveParameterValue), false) 1 D:\wamp64\www\van2130\Sources\Maintenance\Maintenance.php(294): SMF\Maintenance\Tools\Upgrade->welcomeLogin() 2 D:\wamp64\www\van2130\upgrade.php(24): SMF\Maintenance\Maintenance->execute(2) 3 {main} thrown in D:\wamp64\www\van2130\Sources\Maintenance\Maintenance.php on line 661

PHP error log reflects the above. MySQL & Apache logs clear.

Upgrade.log:

2026-08-08T17:11:08+00:00
Step 1: Login
Making sure SMF can write to "D:\wamp64\www\van2130\Settings.php"... done.
Making sure SMF can write to "D:\wamp64\www\van2130\Settings_bak.php"... done.
Making sure SMF can write to "D:\wamp64\www\van2130/custom_avatar"... done.
Making sure SMF can write to "D:/wamp64/www/van2130/cache/db_last_error.php"... done.

@Sesquipedalian

Copy link
Copy Markdown
Member Author

That should be fixed now. The cause was just a silly mistake on my part. If you could test again, @sbulen, that would be lovely!

@sbulen

sbulen commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Note this branch currently has merge conflicts.

Signed-off-by: Jon Stovell <jonstovell@gmail.com>

# Conflicts:
#	Sources/Maintenance/Tools/Install.php
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Can be used to allow us to check a password without automatically updating it. This matters for the upgrader, where we don't want to write to the members table before it is ready to receive new data.

Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Is now idempotent (meaning, running the step again won't change anything) and can handle every possible format that the value might have been in before.

Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
@Sesquipedalian

Copy link
Copy Markdown
Member Author

Fixed now, @sbulen. Ready for a retest whenever you are able.

@albertlast

Copy link
Copy Markdown
Collaborator

I went over this while working on upgrade hardening, because the DropTimeOffset fix in here is a prerequisite for a 2.1 → 3.0 upgrade completing at all on release-3.0. Reporting what I ran and three things I noticed, none of which I think block it.

What I verified

Merged into current release-3.0 (clean, no conflicts), then:

check result
PHP lint, all 25 changed files clean
Unit suite on the merged tree 280 tests, 495 assertions, OK
Full browser upgrade, 2.1.7 baseline → 3.0 Alpha 4 completed
smf_log_errors after that upgrade 0 rows
Upgrade run twice (CLI), schema/row counts/settings diffed second run changed nothing

The browser run is the one that mattered, since that is what this PR is mostly about: login → upgrade options → 245 migration substeps → 72 utf8mb4 substeps → 2 cleanup substeps → finalize → redirect to index.php, with no PHP notices, warnings or deprecations anywhere in the rendered output. The dynamic security token renders and validates, so the Utils::$context switch is doing its job. Point 2 of the description checks out as well — the row that lands is id_member=1, admin, {"version":"SMF 3.0 Alpha 4","member_acted":"admin"}.

I also chased three things that looked like problems and turned out not to be, in case they save anyone else the trip: the guest dataset dropping from Basic to Minimal does not affect login, because Minimal selects mem.* and so still has passwd, password_salt and passwd_flood; the CacheApi::$enable default plus max() is a fix, since displayDbError() raises it before calling CacheApi::get() and reading the uninitialised typed Config::$cache_enable would otherwise throw inside the error handler; and dropping $last_slash from Install.php keeps the CLI fix, just inline via str_contains().

1. A change to password handling that is not in the description

This is the only one I would want a second opinion on before it merges.

In the old Login2::checkPasswordFallbacks(), the SMF-native fallbacks used the decoded password but the other forum software ones used the raw POST value:

// SMF 1.1 / 2.0 — decoded
$other_passwords[] = sha1(strtolower($this->member->username) . Utils::htmlspecialcharsDecode($_POST['passwrd']));

// phpBB3, vBulletin, MyBB, PunBB, … — raw
$other_passwords[] = md5($_POST['passwrd']);
$other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2));
$other_passwords[] = hash('sha256', $_POST['passwrd']);
$other_passwords[] = $_POST['passwrd'];

Security::checkPasswordFallbacks() now uses $password throughout, and Login2 passes Utils::htmlspecialcharsDecode($_POST['passwrd']). So every converted-forum fallback now sees the decoded value where it previously saw the encoded one.

I think the new behaviour is the correct one — the source software hashed the literal password, so & in a password should be & and not &amp;. But it changes whether a converted user whose password contains & < > " ' can log in, for anyone who has not logged in since the conversion, and it is an authentication change that the description does not mention. Worth either a line in the description or a deliberate confirmation that it is intended.

2. AttachmentDirectory loses its guard

isCandidate() is removed, so the migration now runs on every pass. The rewritten execute() is idempotent, so that is safe in itself. Two smaller things about it:

  • When none of the four formats parse, it silently falls back to [1 => Config::$boarddir . '/attachments'], discarding whatever the admin had. For a value that is merely unusual rather than invalid, that points attachments somewhere new without saying so. A logged warning on that branch would make it recoverable.
  • $current_attach_dir = Config::$modSettings['currentAttachmentUploadDir'] ?? array_key_first($attach_dirs) keeps an existing value even when it names a key that is not in $attach_dirs — which is exactly the case after the fallback above.

3. Placeholder docblock

RemoveTempAttachments::error() carries undocumented method. The two language strings it reaches for both exist with the right placeholders (attach_directory_admin_warning takes {attach_dir}, cant_access_upload_path takes {path}), so only the docblock needs a moment.

…orums

Signed-off-by: Jon Stovell <jonstovell@gmail.com>
@Sesquipedalian

Copy link
Copy Markdown
Member Author

1. A change to password handling that is not in the description

This is the only one I would want a second opinion on before it merges.

In the old Login2::checkPasswordFallbacks(), the SMF-native fallbacks used the decoded password but the other forum software ones used the raw POST value:

// SMF 1.1 / 2.0 — decoded
$other_passwords[] = sha1(strtolower($this->member->username) . Utils::htmlspecialcharsDecode($_POST['passwrd']));

// phpBB3, vBulletin, MyBB, PunBB, … — raw
$other_passwords[] = md5($_POST['passwrd']);
$other_passwords[] = crypt($_POST['passwrd'], substr($_POST['passwrd'], 0, 2));
$other_passwords[] = hash('sha256', $_POST['passwrd']);
$other_passwords[] = $_POST['passwrd'];

Security::checkPasswordFallbacks() now uses $password throughout, and Login2 passes Utils::htmlspecialcharsDecode($_POST['passwrd']). So every converted-forum fallback now sees the decoded value where it previously saw the encoded one.

I think the new behaviour is the correct one — the source software hashed the literal password, so & in a password should be & and not &amp;. But it changes whether a converted user whose password contains & < > " ' can log in, for anyone who has not logged in since the conversion, and it is an authentication change that the description does not mention. Worth either a line in the description or a deliberate confirmation that it is intended.

That was an unintentional change, although I do agree that the new behaviour is correct. However, purely for the sake of backward compatibility, I will add some code to try variants of htmlspecialchars($password) within the loop that checks for passwords from other forum software.

Signed-off-by: Jon Stovell <jonstovell@gmail.com>
Signed-off-by: Jon Stovell <jonstovell@gmail.com>
@Sesquipedalian

Copy link
Copy Markdown
Member Author

2. AttachmentDirectory loses its guard

isCandidate() is removed, so the migration now runs on every pass. The rewritten execute() is idempotent, so that is safe in itself. Two smaller things about it:

  • When none of the four formats parse, it silently falls back to [1 => Config::$boarddir . '/attachments'], discarding whatever the admin had. For a value that is merely unusual rather than invalid, that points attachments somewhere new without saying so. A logged warning on that branch would make it recoverable.
  • $current_attach_dir = Config::$modSettings['currentAttachmentUploadDir'] ?? array_key_first($attach_dirs) keeps an existing value even when it names a key that is not in $attach_dirs — which is exactly the case after the fallback above.
  • Those four formats are the only ones considered valid.
  • Good catch. Fixed now.

@Sesquipedalian
Sesquipedalian merged commit 8f380cd into SimpleMachines:release-3.0 Sep 5, 2026
9 checks passed
@Sesquipedalian
Sesquipedalian deleted the 3.0/upgrader_login branch September 5, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants